package com.hsb.seckill.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.util.concurrent.RateLimiter;
import com.hsb.seckill.config.AccessLimit;
import com.hsb.seckill.entity.Order;
import com.hsb.seckill.entity.SeckillOrder;
import com.hsb.seckill.entity.User;
import com.hsb.seckill.rabbitmq.MQSender;
import com.hsb.seckill.result.ResBean;
import com.hsb.seckill.result.ResBeanEnum;
import com.hsb.seckill.service.GoodsService;
import com.hsb.seckill.service.OrderService;
import com.hsb.seckill.service.SeckillOrderService;
import com.hsb.seckill.vo.GoodsVo;
import com.hsb.seckill.vo.SeckillMessage;
import com.rabbitmq.tools.json.JSONUtil;
import com.sun.org.apache.xpath.internal.operations.Or;
import com.wf.captcha.ArithmeticCaptcha;
import org.redisson.Redisson;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean{

    @Autowired
    private GoodsService goodsService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MQSender mqSender;
    @Autowired
    private SeckillOrderService seckillOrderService;
    @Autowired
    private DefaultRedisScript<Long> redisScript;

    private final Map<Long,Boolean> emptyStockMap = new HashMap<>();


    /**
     * 商品秒杀
     */
    @Transactional
    @RequestMapping(value = "/doSeckill",method = RequestMethod.POST)
    @ResponseBody
    public ResBean seckill2(User user, long goodsId){
        if (null == user){
            return ResBean.error(ResBeanEnum.USER_NOT_LOGIN);
        }

        // 从redis获取订单,判断是否有重复用户抢购
        //SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        //if (seckillOrder != null){
        //    return ResBean.error(ResBeanEnum.REPEATE_ERROR);
        //}

        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent("seckillCount:" + user.getId() + ":" + goodsId, user.getId());
        if (!ifAbsent){
            return ResBean.error(ResBeanEnum.REPEATE_ERROR);
        }

        //Integer count = (Integer) redisTemplate.opsForValue().get("seckillCount:"+user.getId()+":"+goodsId);
        //if (count == null){
        //    redisTemplate.opsForValue().set("seckillCount:"+user.getId()+":"+goodsId,1);
        //}else if (count==1){
        //    return ResBean.error(ResBeanEnum.REPEATE_ERROR);
        //}


        //判断该商品是否有库存，通过内存标记，减少redis的访问
        if (emptyStockMap.get(goodsId)){
            return ResBean.error(ResBeanEnum.EMPTY_STOCK);
        }

        //redis中的商品库存减1
        Long stock = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);

        //Long stock = (Long) redisTemplate.execute(redisScript, Collections.singletonList("seckillGoods:" + goodsId), Collections.EMPTY_LIST);
        //判断当前商品库存是否小于0
        if (stock<0){
            emptyStockMap.put(goodsId,true);
            //使库存为0
            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
            return ResBean.error(ResBeanEnum.EMPTY_STOCK);
        }

        //rabbitmq发送消息，异步处理下订单
        SeckillMessage seckillMessage = new SeckillMessage(user,goodsId);
        //将Object转为JSONString
        String msg = JSON.toJSONString(seckillMessage);
        mqSender.sendSeckillMessage(msg);
        return ResBean.success(0);
    }





    /**
     * 商品秒杀
     */
    @RequestMapping(value = "/{path}/doSeckill",method = RequestMethod.POST)
    @ResponseBody
    public ResBean seckill(@PathVariable("path") String path, User user, long goodsId){
        if (null == user){
            return ResBean.error(ResBeanEnum.USER_NOT_LOGIN);
        }

        // 校验秒杀路径
        Boolean check = orderService.checkPath(user,goodsId,path);
        if (!check){
            return ResBean.error(ResBeanEnum.REQUEST_ILLEGAL);
        }

        // 从redis获取订单,判断是否有重复用户抢购
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if (seckillOrder != null){
            return ResBean.error(ResBeanEnum.REPEATE_ERROR);
        }

        //判断该商品是否有库存，通过内存标记，减少redis的访问
        if (emptyStockMap.get(goodsId)){
            return ResBean.error(ResBeanEnum.EMPTY_STOCK);
        }

        //redis中的商品库存减1
        Long stock = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);

        //Long stock = (Long) redisTemplate.execute(redisScript, Collections.singletonList("seckillGoods:" + goodsId), Collections.EMPTY_LIST);
        //判断当前商品库存是否小于0
        if (stock<0){
            emptyStockMap.put(goodsId,true);
            //使库存为0
            //redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
            return ResBean.error(ResBeanEnum.EMPTY_STOCK);
        }

        //rabbitmq发送消息，异步处理下订单
        SeckillMessage seckillMessage = new SeckillMessage(user,goodsId);
        //将Object转为JSONString
        String msg = JSON.toJSONString(seckillMessage);
        mqSender.sendSeckillMessage(msg);
        return ResBean.success(0);
    }

    /**
     * 获取秒杀结果
     * orderId：成功，-1:失败，0:排队中
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "/result",method = RequestMethod.GET)
    @ResponseBody
    public ResBean getResult(User user,Long goodsId){
        if (user == null){
            return ResBean.error(ResBeanEnum.USER_NOT_LOGIN);
        }
        // 获取秒杀结果
        Long orderId = seckillOrderService.getResult(user,goodsId);
        return ResBean.success(orderId);
    }



    /**
     * 获取秒杀地址
     * @param user
     * @param goodsId
     * @return
     */
    //@AccessLimit(second = 5,maxCount=5,needLogin=true)
    @RequestMapping(value = "/getPath",method = RequestMethod.GET)
    @ResponseBody
    public ResBean getPath(User user,Long goodsId,String captcha,HttpServletRequest request){
        // 判断用户是否登录
        if (user == null){
            return ResBean.error(ResBeanEnum.USER_NOT_LOGIN);
        }

        // 校验验证码
        Boolean check = orderService.checkCaptcha(user,goodsId,captcha);
        if (!check){
            return ResBean.error(ResBeanEnum.CAPTCHA_ERROR);
        }
        // 获取秒杀地址
        String str = orderService.getPath(user,goodsId);
        return ResBean.success(str);
    }

    /**
     * 生成验证码
     * @param user
     * @param goodsId
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "/captcha",method = RequestMethod.GET)
    public void captcha(User user,Long goodsId,HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 设置请求头为输出图片类型
        response.setContentType("image/jpg");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        // 算术类型
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 32);
        captcha.setLen(3);  // 几位数运算，默认是两位
        captcha.getArithmeticString();  // 获取运算的公式：3+2=?
        captcha.text();  // 获取运算的结果：5

        //将验证码存到redis中
        redisTemplate.opsForValue().set("captcha:"+user.getId()+":"+goodsId,captcha.text());
        // 输出图片流
        captcha.out(response.getOutputStream());
    }


    /**
     * 系统初始化，把商品库存数量加载到redis中
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        //获取所有秒杀商品
        List<GoodsVo> list = goodsService.querySeckillGoods();
        // 判断是否为空
        if (CollectionUtils.isEmpty(list)){
            return;
        }

        list.forEach(goodsVo -> {
            // 初始化内存标记商品不为空
            emptyStockMap.put(goodsVo.getId(),false);
            // 把商品库存数量加载到redis中
            redisTemplate.opsForValue().set("seckillGoods:"+goodsVo.getId(),goodsVo.getStockCount());
        });
    }
}
